#include <windows.h>
#include <mmsystem.h>
#include <string.h>
#include <math.h>
#include "logger.h"
#include "pcm-player.h"

#define WAV_BUFFER_NUM  (8)
#define WAV_BUFFER_SIZE (8192 * 4)

static HWAVEOUT hWaveOut;
static WAVEFORMATEX waveFormat;
static HANDLE hWaveEvent;
static WAVEHDR waveHeader[WAV_BUFFER_NUM];
static unsigned char buffer[WAV_BUFFER_NUM][WAV_BUFFER_SIZE]; // 修正为二维数组
static int current_header = 0;

static int check_winmm_support() {
    MMRESULT result;
    WAVEOUTCAPS waveCaps;
    UINT numDevices;

    // 获取音频输出设备数量
    numDevices = waveOutGetNumDevs();
    if (numDevices == 0) {
        dlog("No wave output devices found (winmm not supported)\n");
        return 0; // 无设备，不支持
    }

    // 检查第一个设备的能力
    result = waveOutGetDevCaps(0, &waveCaps, sizeof(WAVEOUTCAPS));
    if (result != MMSYSERR_NOERROR) {
        dlog("winmm not supported: %d\n", result);
        return 0;
    }

    dlog("winmm is supported! Found %u devices\n", numDevices);
    return 1; // 支持
}

static int pcmPlayer_init(int channels, int samplerate, int bits_per_sample) {
    MMRESULT result;

    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
    waveFormat.nChannels = channels;
    waveFormat.nSamplesPerSec = samplerate;
    waveFormat.wBitsPerSample = bits_per_sample;
    waveFormat.nBlockAlign = (channels * bits_per_sample) / 8;
    waveFormat.nAvgBytesPerSec = samplerate * waveFormat.nBlockAlign;
    waveFormat.cbSize = 0;

    hWaveEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    result = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, (DWORD_PTR)hWaveEvent , 0, CALLBACK_EVENT);
    if (result != MMSYSERR_NOERROR) {
        elog("Failed to open waveform output device. errcode: %d\n", result);
        return -1;
    }

    for(int i = 0; i < WAV_BUFFER_NUM; i++) {
        memset(&waveHeader[i], 0, sizeof(WAVEHDR));
        waveHeader[i].lpData = (LPSTR)buffer[i]; // 修正buffer引用方式
        waveHeader[i].dwBufferLength = WAV_BUFFER_SIZE; // 使用明确的大小
        waveHeader[i].dwFlags = 0;
        waveHeader[i].dwLoops = 1;
    }
    
    current_header = 0;

    ilog("Windows mmsystem Audio player initialized\n");
    return 0;
}

static int pcmPlayer_getParams(int *channels, int *samplerate, int *bits_per_sample) {
    if (!channels || !samplerate || !bits_per_sample) {
        return -1;
    }

    *channels = waveFormat.nChannels;
    *samplerate = waveFormat.nSamplesPerSec;
    *bits_per_sample = waveFormat.wBitsPerSample;

    return 0;
}

static int pcmPlayer_setVolume(int db) {
    if (!hWaveOut) {
        elog("hWaveOut is NULL\n");
        return -1;
    }
    // 获得当前音量: 高16位表示右声道音量，低16位表示左声道音量
    DWORD vol;
    waveOutGetVolume(hWaveOut, (LPDWORD)&vol);
    DWORD leftVol = (vol & 0xffffUL);  // 只处理左声道音量
    dlog("Current volume %d\n", leftVol);
    
    // 根据分贝调整音量
    double factor = pow(10.0, db / 20.0);
    DWORD newLeftVol = (DWORD)(leftVol * factor);
    if(db > 0 && newLeftVol == leftVol) newLeftVol++; // 确保音量有变化
    if(newLeftVol > 0xffffUL) newLeftVol = 0xffffUL;
    
    // 设置两个声道相同的音量
    MMRESULT result = waveOutSetVolume(hWaveOut, (newLeftVol<<16) | newLeftVol);
    if (result != MMSYSERR_NOERROR) {
        elog("Failed to set volume. errcode: %d\n", result);
        return -1;
    }

    dlog("Volume set to %d\n", newLeftVol);

    // 返回归一化的音量值(0-100)
    return (int)(newLeftVol * 100 / 0xffff);
}

static int pcmPlayer_write(void *pcm_data, int size) {
    if (!hWaveOut) {
        elog("hWaveOut is NULL\n");
        return -1;
    }

    // 检查大小是否超过缓冲区
    if (size > WAV_BUFFER_SIZE) {
        elog("PCM data size %d exceeds buffer size %d\n", size, WAV_BUFFER_SIZE);
        size = WAV_BUFFER_SIZE; // 截断数据
    }

    MMRESULT result;

    // wait for the current buffer to finish playing, then unprepare it, then copy caller function pcm data
    if(waveHeader[current_header].dwFlags & (WHDR_PREPARED)) {
        while(waveOutUnprepareHeader(hWaveOut, &waveHeader[current_header], sizeof(WAVEHDR)) == WAVERR_STILLPLAYING) {
            int time1 = timeGetTime();
            WaitForSingleObject(hWaveEvent, INFINITE);
            int time2 = timeGetTime();
            dlog("Buffer %d done, wait time %d ms\n", current_header, time2 - time1);
        }
        dlog("Buffer %d done, Unprepared\n", current_header);
    }

    waveHeader[current_header].lpData = (LPSTR)buffer[current_header]; // 修正buffer引用
    memcpy(waveHeader[current_header].lpData, pcm_data, size);
    waveHeader[current_header].dwBufferLength = size;
    waveHeader[current_header].dwFlags = 0;
    waveHeader[current_header].dwLoops = 1;

    result = waveOutPrepareHeader(hWaveOut, &waveHeader[current_header], sizeof(WAVEHDR));
    if (result != MMSYSERR_NOERROR) {
        elog("Failed to prepare waveform data. errcode: %d\n", result);
        return -1;
    }

    // write the current buffer
    dlog("Mov pcm to buffer %d wait play\n", current_header);
    result = waveOutWrite(hWaveOut, &waveHeader[current_header], sizeof(WAVEHDR));
    if (result != MMSYSERR_NOERROR) {
        elog("Failed to write waveform data. errcode: %d\n", result);
        return -1;
    }

    // move to next buffer
    current_header = (current_header + 1) % WAV_BUFFER_NUM;
    return 0;
}

static int pcmPlayer_deinit() {
    if(!hWaveOut) {
        elog("hWaveOut is NULL\n");
        return -1;
    }

    // 先停止播放
    waveOutReset(hWaveOut);
    
    // 确保所有缓冲区都被unprepare
    for (int i = 0; i < WAV_BUFFER_NUM; i++) {
        if (waveHeader[i].dwFlags & WHDR_PREPARED) {
            waveOutUnprepareHeader(hWaveOut, &waveHeader[i], sizeof(WAVEHDR));
        }
    }

    MMRESULT result;
    while(1) {
        result = waveOutClose(hWaveOut);
        if(result !=  WAVERR_STILLPLAYING) break;  // wait for all buffers to finish playing
        Sleep(100);
    }

    hWaveOut = NULL;

    if (result != MMSYSERR_NOERROR) {
        elog("Failed to close waveform output device. errcode: %d\n", result);
        return -1;
    }

    CloseHandle(hWaveEvent);
    hWaveEvent = NULL;
    
    dlog("Windows mmsystem Audio player deinitialized\n");

    return 0;
}

static pcm_player_t pcm_player = {
    .platform = WINNT,
    .name = "Windows mmsystem",
    .check = check_winmm_support,
    .init = pcmPlayer_init,
    .getParams = pcmPlayer_getParams,
    .setVolume = pcmPlayer_setVolume,
    .write = pcmPlayer_write,
    .ctrl = NULL,
    .deinit = pcmPlayer_deinit,
};

__attribute__((constructor(101)))  //优先级101，把mm接口优先注册
static void auto_register_pcm_player(void) {
    pcmPlayer_register(&pcm_player);
}
